#include <string.h>
#include <math.h>
#include "render_util.h"
#include "game.h"

static void animation(Player* self);

void player_init(Player* player) {
    memset(player, 0, sizeof(Player));

    player->x = 14 * 8;
    player->y = 26 * 8 + 4;
    player->sprite = SPRITE_PACMAN;
    player->speed = 0.8f;

    player->dx = 1;
    player->dy = 0;
    player->direction = DIR_RIGHT;
    
    player->next_dx = 1;
    player->next_dy = 0;
    player->next_direction = DIR_RIGHT;

    player->stop_timer = 0;
    player->frame = 0;
    player->is_on_wall = FALSE;
    player->die_anim = FALSE;
    player->die_anim_time = 0;
    player->pellet_eat = 0;

    animation(player);
}

static void die_animation(Player* self) {
    int time = (self->die_anim_time - 30) / 8;
    if (time < 0) time = 0;
    if (time > 11) time = 11;
    self->sprite = SPRITE_PACMAN_DEATH + time;
}

static void animation(Player* self) {
    static int HORIZ_FRAMES[] = { 4, 2, 0, 2 };
    static int VERT_FRAMES[] = { 4, 3, 1, 3};

    int waka_frame = self->frame / 2 % 4;

    if (self->direction == DIR_RIGHT || self->direction == DIR_LEFT) {
        self->sprite = SPRITE_PACMAN + HORIZ_FRAMES[waka_frame];
    } else {
        self->sprite = SPRITE_PACMAN + VERT_FRAMES[waka_frame];
    }

    if (!self->is_on_wall)
        self->frame++;
}

void player_begin_die_animation(Player* self) {
    self->die_anim = TRUE;
    self->die_anim_time = 0;
}

void player_update_inputs(Player* self, Game* game) {
    // set buffered direction
    if (game->interface->inputs & INPUT_RIGHT) {
        self->next_direction = DIR_RIGHT;
        self->next_dx = 1;
        self->next_dy = 0;
    } else if (game->interface->inputs & INPUT_UP) {
        self->next_direction = DIR_UP;
        self->next_dx = 0;
        self->next_dy = -1;
    } else if (game->interface->inputs & INPUT_DOWN) {
        self->next_direction = DIR_DOWN;
        self->next_dx = 0;
        self->next_dy = 1;
    } else if (game->interface->inputs & INPUT_LEFT) {
        self->next_direction = DIR_LEFT;
        self->next_dx = -1;
        self->next_dy = 0;
    }
}

void player_update(Player* self, Game* game) {
    if (self->die_anim) {
        die_animation(self);
        self->die_anim_time++;
        return;
    }

    if (game->eaten_ghost) return;
    if (game->player_die) return;

    if (self->stop_timer > 0) {
        self->stop_timer--;
        return;
    }

    // check if player can go in direction
    if (!game_is_tile_solid(game, obj_cell_x(self) + self->next_dx, obj_cell_y(self) + self->next_dy)) {
        self->dx = self->next_dx;
        self->dy = self->next_dy;
        self->direction = self->next_direction;
    }

    if (game_player_has_power_pill(game)) {
        self->speed = LEVEL_SPECS[game->level_index].player_power_speed;
    } else {
        self->speed = LEVEL_SPECS[game->level_index].player_speed;
    }

    self->subpixel += self->speed * REFERENCE_SPEED;
    self->x += self->dx * (int)self->subpixel;
    self->y += self->dy * (int)self->subpixel;
    self->subpixel = fmodf(self->subpixel, 1.0f);

    // cornering movement
    if (self->dx == 0) {
        if (self->x < obj_cell_x(self) * 8 + 4)
            self->x++;
        else if (self->x > obj_cell_x(self) * 8 + 4)
            self->x--;
    }

    if (self->dy == 0) {
        if (self->y < obj_cell_y(self) * 8 + 4)
            self->y++;
        else if (self->y > obj_cell_y(self) * 8 + 4)
            self->y--;
    }

    // wall collision
    self->is_on_wall = FALSE;

    if (game_is_tile_solid(game, (self->x + self->dx * 4) / 8, (self->y + self->dy * 4) / 8)) {
        self->is_on_wall = TRUE;
        if (self->dx != 0)
            self->x = obj_cell_x(self) * 8 + 4;

        if (self->dy != 0)
            self->y = obj_cell_y(self) * 8 + 4;
    }

    // Wakka Wakka some dots
    if (game_get_tile(game, obj_cell_x(self), obj_cell_y(self)) == GAME_TILE_PELLET) {
        self->stop_timer = 1;
        game_set_tile(game, obj_cell_x(self), obj_cell_y(self), GAME_TILE_EMPTY);
        game_player_ate_dot(game);

        // play pellet eat sound
        self->pellet_eat = (self->pellet_eat + 1) % 2;

        sound_stop(&game->interface->sound, SOUND_EAT1);
        sound_stop(&game->interface->sound, SOUND_EAT2);

        if (self->pellet_eat == 0)
            sound_start(&game->interface->sound, SOUND_EAT1);
        else
            sound_start(&game->interface->sound, SOUND_EAT2);
    }

    // Wakka Wakka a power pellet
    if (game_get_tile(game, obj_cell_x(self), obj_cell_y(self)) == GAME_TILE_POWER_PELLET) {
        self->stop_timer = 3;
        game_set_tile(game, obj_cell_x(self), obj_cell_y(self), GAME_TILE_EMPTY);
        game_player_ate_power_pellet(game);
    }

    // screen wrapping
    if (obj_cell_x(self) >= 30)
        self->x = -8;

    if (obj_cell_x(self) <= -3)
        self->x = 28 * 8 + 4;

    animation(self);
}

void player_draw(Player* self, DisplayUtilState* render) {
    if (self->die_anim) {
        render->flip_x = FALSE;
        render->flip_y = FALSE;
    } else {
        render->flip_x = self->direction == DIR_LEFT;
        render->flip_y = self->direction == DIR_UP;
    }

    render->palette = 9;
    display_sprite(render, self->x - 8, self->y - 8, self->sprite);
}